/**
 * 工具包头文件 for C++
 */

#pragma once

#include <stddef.h>
#include <stdint.h>
#include <new>
#include "std_err.h"
#include "utils.h"

// 删除指针指向的对象，并将指针置为 NULL
#define DELPOBJ(p)  do { if(p) { delete (p); p = NULL; } } while(0)

/**
 * 创建一个指定类的实例（与 new 操作符类似，但它在类构造函数发生异常时，会自动释放分配的内存，并返回 NULL，而不抛出异常）
 * @param T 要创建实例的类
 * @param ... 要使用的类构造函数参数列表
 */
#define CREATE_INSTANCE(T, ...)  do {		\
	void* mem = malloc0(sizeof(T));			\
	if(!mem) return NULL;					\
	try {									\
		return new(mem) T(__VA_ARGS__);	    \
	} catch(...) {							\
		free(mem); return NULL;				\
	}										\
} while(0)

/**
 * 创建一个指定类的实例并保存其指针到指定变量指针（与 new 操作符类似，但它在类构造函数发生异常时，会自动释放分配的内存，并返回 NULL，而不抛出异常）
 * @param pobj 用于保存实例指针
 * @param T 要创建实例的类
 * @param ... 要使用的类构造函数参数列表
 */
#define CREATE_INSTANCE2(pobj, T, ...)  do {\
	pobj = NULL;                            \
	void* mem = malloc0(sizeof(T));			\
	if(mem) try {							\
		pobj = new(mem) T(__VA_ARGS__);	    \
	} catch(...) {							\
		free(mem);							\
	}										\
} while(0)


/**
 * 文件接口定义
 */
struct osfile {
	protected: char _filepath[512];
	protected: osfile() { _filepath[0] = '\0'; }
	protected: osfile(const char *filepath);
	public: virtual const char* filepath() { return _filepath; }
	public: virtual std_err_t sync() = 0;
	public: virtual std_err_t ioctl(int cmd, void *opt, size_t opt_size) = 0;
	public: virtual std_err_t read(int64_t offset, void* buf, uint64_t size, uint64_t* plen) = 0;
	public: virtual std_err_t read_ring(size_t ring_base, size_t ring_size, size_t tail, void *dst, size_t size) = 0;
	public: virtual std_err_t write(int64_t offset, const void* buf, uint64_t size, uint64_t* plen) = 0;
	public: virtual std_err_t write_ring(size_t ring_base, size_t ring_size, size_t tail, const void *_src, size_t size) = 0;
};


/**
 * 寄存器访问工具类
 */
template <typename REGST> class regacc {
    protected: osfile* _of = NULL;
    // 寄存器在文件中的偏移地址
    protected: uint64_t _regbase = 0;
    // 寄存器在 RAM 中的缓存
    protected: REGST* _regst = NULL;

	public:    virtual REGST* regst() { return _regst; }
	public:    virtual osfile* of() { return _of; }
    public:    virtual std_err_t load() { return _of->read(_regbase, _regst, sizeof(REGST), NULL); }
    public:    virtual std_err_t save() { return _of->write(_regbase, _regst, sizeof(REGST), NULL); }
    public:    virtual uint32_t read32(uint64_t regaddr) { uint32_t val; return _of->read(_regbase + regaddr, &val, sizeof(val), NULL) == STD_ERR_OK ? val : 0; }
    public:    virtual uint64_t read64(uint64_t regaddr) { uint64_t val; return _of->read(_regbase + regaddr, &val, sizeof(val), NULL) == STD_ERR_OK ? val : 0; }
    public:    virtual void write32(uint64_t regaddr, uint32_t val) { _of->write(_regbase + regaddr, &val, sizeof(val), NULL); }
    public:    virtual void write64(uint64_t regaddr, uint64_t val) { _of->write(_regbase + regaddr, &val, sizeof(val), NULL); }
};

// 计算 寄存器偏移地址
#define REG_OFFSET(regacc, field)           (((size_t)&regacc->_regst->field) - ((size_t)regacc->_regst))
// 计算 寄存器空间中某个字段的偏移地址
#define REG_ADDR(regacc, field)             (regacc->_regbase + REG_OFFSET(regacc, field))

// 寄存器访问
#define REG(regacc, field)                  (regacc->_regst->field)
#define REGR32(regacc, field)               (regacc->_regst->field = regacc->read32(REG_ADDR(regacc, field)))
#define REGR64(regacc, field)               (regacc->_regst->field = regacc->read64(REG_ADDR(regacc, field)))
#define REGW32(regacc, field, val)          (regacc->write32(REG_ADDR(regacc, field), (regacc->_regst->field = (val))))
#define REGW64(regacc, field, val)          (regacc->write64(REG_ADDR(regacc, field), (regacc->_regst->field = (val))))
